<?php

namespace app\shop\controller;

use AopSDK\request\AlipayTradePayRequest;
use AopSDK\request\AlipayTradeRefundRequest;
use think\facade\Db;
use Wxpay\lib\WxPayCloseOrder;
use Wxpay\lib\WxPayMicroPay;
use Wxpay\lib\WxPayOrderQuery;
use Wxpay\lib\WxPayRefund;
use Wxpay\WxPayConfig;
use Wxpay\lib\WxPayApi;

class Codepay extends Base
{
    private $pageStartTime = 0;

    public function dopay()
    {
        $this->pageStartTime = microtime(true);

        $shop_id = self::$shop['id'];
        $code = input('code', '', 'trim');
        $remark = input('remark', '', 'trim');
        $total_fee = input('money', 0, 'trim');
        $total_fee = intval(floatval($total_fee) * 100) / 100;
        if (empty($shop_id) || empty($code) || empty($total_fee)) {
            return errorJson('参数错误');
        }
        $shop = Db::name('shop')
            ->where('id', $shop_id)
            ->find();

        $num = substr($code, 0, 2);
        if ($num >= 25 && $num <= 30) { //支付宝付款码支付
            $pay_type = 'alipay';
        } else if ($num >= 10 && $num <= 15) { //微信的付款码支付
            $pay_type = 'wxpay';
        } else {
            return errorJson('请扫描微信或支付宝付款码');
        }
        $out_trade_no = $this->createOrderNo($pay_type);
        $order_id = Db::name('order')->insertGetId([
            'agent_id' => $shop['agent_id'],
            'shop_id' => $shop_id,
            'qrcode_id' => 0,
            'user_id' => '',
            'out_trade_no' => $out_trade_no,
            'transaction_id' => '',
            'total_fee' => $total_fee * 100,
            'order_type' => 'pay',
            'pay_type' => $pay_type,
            'remark' => $remark,
            'status' => 0, // 0-待付款；1-成功；2-失败
            'add_time' => time()
        ]);

        if ($order_id) {
            if ($pay_type == 'alipay') {
                $payConfig = getShopPayConfig($shop, 'alipay');
                if (empty($payConfig)) {
                    return errorJson('未开通支付宝支付');
                }
                $this->alipay($out_trade_no, $shop, $code, $total_fee);

            } elseif ($pay_type == 'wxpay') {
                $this->wxpay($out_trade_no, $shop, $code, $total_fee);
            } else {
                return errorJson('不支持的付款码');
            }
        }

    }

    /**
     * 微信支付
     */
    private function wxpay($out_trade_no, $shop, $code, $total_fee)
    {
        $payConfig = getShopPayConfig($shop, 'wxpay');
        if (empty($payConfig) || $payConfig['status'] == 0) {
            return errorJson('未开通微信收款');
        }

        $config = new WxPayConfig();
        $config->SetAppId($payConfig['appid']);
        $config->SetAppSecret($payConfig['appsecret']);
        $config->SetMerchantId($payConfig['mch_id']);
        if ($payConfig['type'] == 'server') {
            $config->SetSubMchID($payConfig['sub_mch_id']);
        }
        $config->SetKey($payConfig['key']);
        $config->SetSignType('HMAC-SHA256');

        $input = new WxPayMicroPay();
        $input->SetOut_trade_no($out_trade_no);
        $input->SetTotal_fee($total_fee * 100);
        $input->SetAuth_code($code);
        $input->SetBody('扫码收款');
        $input->SetAttach('扫码收款');
        $input->SetTime_start(date('YmdHis'));
        $input->SetTime_expire(date('YmdHis', time() + 600));
        if ($payConfig['type'] == 'server') {
            $input->SetSubMchID($payConfig['sub_mch_id']);
        }

        $WxPayApi = new WxPayApi();
        $result = $WxPayApi->micropay($config, $input);

        // file_put_contents("./wxpay_result.txt", json_encode($result) . "\n\n", 8);

        if (isset($result['return_code']) && $result['return_code'] == 'SUCCESS' && isset($result['result_code']) && $result['result_code'] == 'SUCCESS') {
            $this->wxpaySuccess($out_trade_no, $payConfig, $shop, $result);
        } else if (isset($result['err_code']) && isset($result['return_code']) && isset($result['return_msg']) && $result['err_code'] == 'USERPAYING' && $result['return_code'] == 'SUCCESS' && $result['return_msg'] == 'OK') {
            // 用户支付中
            $this->wxpayOrderQuery($out_trade_no, $payConfig, $shop);
        } else {
            $this->wxpayFail($out_trade_no, $result);
        }
    }

    /**
     * 支付宝支付
     */
    private function alipay($out_trade_no, $shop, $code, $total_fee)
    {
        return errorJson('支付宝扫码收款功能升级中，暂不支持');
        /*$payConfig = getShopPayConfig($shop, 'alipay');
        if (empty($payConfig) || $payConfig['status'] == 0) {
            return errorJson('未开通支付宝收款');
        }

        $aop = get_alipay_aop($payConfig);
        $request = new AlipayTradePayRequest();
        $request->setBizContent(json_encode([
            'out_trade_no' => $out_trade_no,
            'auth_code' => $code,
            'subject' => $shop['title'],
            'total_amount' => $total_fee,
            'scene' => 'bar_code',
            'query_options' => ['fund_bill_list', 'voucher_detail_list', 'discount_goods_detail']
        ]));
        if ($payConfig['type'] == 'server') {
            $result = $aop->execute($request, '', $payConfig['alipay_token']);
        } else {
            $result = $aop->execute($request);
        }
        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {          //交易成功
            $result = json_decode(json_encode($result), true)['alipay_trade_pay_response'];
            $this->alipaySuccess($out_trade_no, $payConfig, $shop, $result);
        } else if (!empty($resultCode) && $resultCode == 10003) {   //需要用户输入支付密码
            $this->alipayOrderQuery($out_trade_no, $payConfig, $shop);
        } else {                                                    //交易失败
            $this->alipayFail($out_trade_no, $result->$responseNode->sub_msg);
        }*/

        /*
        if (isset($result['return_code']) && $result['return_code'] == 'SUCCESS' && isset($result['result_code']) && $result['result_code'] == 'SUCCESS') {
            $this->wxpaySuccess($out_trade_no, $payConfig, $shop, $result);
        } else if (isset($result['err_code']) && isset($result['return_code']) && isset($result['return_msg']) && $result['err_code'] == 'USERPAYING' && $result['return_code'] == 'SUCCESS' && $result['return_msg'] == 'OK') {
            // 用户支付中
            $this->wxpayOrderQuery($out_trade_no, $payConfig, $shop);
        } else {
            $this->wxpayFail($out_trade_no, $result);
        }*/
    }

    /*public function testQuery()
    {
        $out_trade_no = 'W202112270116132427';
        $shop = Db::name('shop')
            ->where('id', self::$shop['id'])
            ->find();
        $payConfig = getShopPayConfig($shop, 'wxpay');

        $this->wxpayOrderQuery($out_trade_no, $payConfig, $shop);
    }*/

    /**
     * 轮询用户支付状态
     */
    private function wxpayOrderQuery($out_trade_no, $payConfig, $shop)
    {
        $input = new WxPayOrderQuery();
        $input->SetOut_trade_no($out_trade_no);
        if ($payConfig['type'] == 'server') {
            $input->SetSubMchID($payConfig['sub_mch_id']);
        }

        $config = new WxPayConfig();
        $config->SetAppId($payConfig['appid']);
        $config->SetAppSecret($payConfig['appsecret']);
        $config->SetMerchantId($payConfig['mch_id']);
        if ($payConfig['type'] == 'server') {
            $config->SetSubMchID($payConfig['sub_mch_id']);
        }
        $config->SetKey($payConfig['key']);
        $config->SetSignType('HMAC-SHA256');

        $WxPayApi = new WxPayApi();
        $result = $WxPayApi->orderQuery($config, $input);
        if ($result['trade_state'] == 'USERPAYING' || (isset($result['trade_code']) && $result['trade_code'] == 'USERPAYING')) {
            // 用户支付中
            $runtime = $this->getRunTime();
            if ($runtime < 60) {
                usleep(1500000);
                $this->wxpayOrderQuery($out_trade_no, $payConfig, $shop);
            } else {
                // 撤销订单
                $this->wxpayCloseOrder($out_trade_no, $payConfig);
            }
        } elseif ($result['trade_state'] == 'SUCCESS' || (isset($result['trade_code']) && $result['trade_code'] == 'SUCCESS')) {
            // 支付成功
            $this->wxpaySuccess($out_trade_no, $payConfig, $shop, $result);
        } else {
            // 支付失败
            $this->wxpayFail($out_trade_no, ['err_code_des' => '用户取消支付']);
        }
    }

    private function wxpayCloseOrder($out_trade_no, $payConfig)
    {
        $input = new WxPayCloseOrder();
        $input->SetOut_trade_no($out_trade_no);
        if ($payConfig['type'] == 'server') {
            $input->SetSubMchID($payConfig['sub_mch_id']);
        }

        $config = new WxPayConfig();
        $config->SetAppId($payConfig['appid']);
        $config->SetAppSecret($payConfig['appsecret']);
        $config->SetMerchantId($payConfig['mch_id']);
        if ($payConfig['type'] == 'server') {
            $config->SetSubMchID($payConfig['sub_mch_id']);
        }
        $config->SetKey($payConfig['key']);
        $config->SetSignType('HMAC-SHA256');

        $WxPayApi = new WxPayApi();
        $WxPayApi->closeOrder($config, $input);

        $this->wxpayFail($out_trade_no, ['return_msg' => '支付失败，已取消']);
    }

    private function getRunTime()
    {
        $etime = microtime(true);
        $total = $etime - $this->pageStartTime;
        return round($total, 4);
    }


    private function wxpaySuccess($out_trade_no, $payConfig, $shop, $result)
    {
        // 优惠信息
        $settlement_total_fee = $result['total_fee'];
        $coupon_count = 0;
        $coupon_fee_nocash = 0;
        $coupon_fee_cash = 0;
        if (!empty($result['coupon_count'])) {
            if (isset($result['coupon_type_0']) && $result['coupon_type_0'] == 'NO_CASH') {
                $coupon_fee_nocash = $result['coupon_fee_0'];
            }
            if (isset($result['coupon_type_0']) && $result['coupon_type_0'] == 'CASH') {
                $coupon_fee_cash = $result['coupon_fee_0'];
            }
            if (isset($result['coupon_type_1']) && $result['coupon_type_1'] == 'NO_CASH') {
                $coupon_fee_nocash = $result['coupon_fee_1'];
            }
            if (isset($result['coupon_type_1']) && $result['coupon_type_1'] == 'CASH') {
                $coupon_fee_cash = $result['coupon_fee_1'];
            }
            if (empty($coupon_fee_cash) && empty($coupon_fee_nocash)) {
                $coupon_fee_cash = $result['coupon_fee_0'];
            }
            $coupon_count = $result['coupon_count'];
            if (isset($result['settlement_total_fee'])) {
                $settlement_total_fee = $result['settlement_total_fee'];
            }
        }
        //营销详情
        if (!empty($result['promotion_detail'])) {
            $promotion_detail = json_decode($result['promotion_detail'], true);
            $coupon_count = count($promotion_detail['promotion_detail']);
            foreach ($promotion_detail['promotion_detail'] as $k => $v) {
                if ($v['type'] == 'DISCOUNT') {
                    $coupon_fee_nocash += ($v['amount'] * 100);
                } else {
                    $coupon_fee_cash += ($v['amount'] * 100);
                }
            }
            $settlement_total_fee = $result['total_fee'] * 100 - $coupon_fee_nocash;
        }
        // 计算手续费
        $fee = $this->calcOrderFee($payConfig['rate'], $settlement_total_fee / 100);
        // 计算代理分润
        if ($shop['agent_id'] && $fee > 0) {
            $rebate = Db::name('agent')
                ->where('id', $shop['agent_id'])
                ->value('rebate');
            $fee_agent = $this->calcAgentFee($payConfig['rate'], $settlement_total_fee / 100, intval($rebate));
        } else {
            $fee_agent = 0;
        }

        Db::name('order')
            ->where('out_trade_no', $out_trade_no)
            ->update([
                'status' => 1,
                'transaction_id' => $result ['transaction_id'],
                'coupon_count' => $coupon_count,
                'coupon_fee_nocash' => $coupon_fee_nocash,
                'coupon_fee_cash' => $coupon_fee_cash,
                'settlement_total_fee' => $settlement_total_fee,
                'fee' => $fee * 100,
                'fee_agent' => $fee_agent * 100,
                'pay_time' => strtotime($result['time_end'])
            ]);

        return successJson('', '收款成功');
    }

    /**
     * 支付失败
     */
    private function wxpayFail($out_trade_no, $result = [])
    {
        Db::name('order')
            ->where('out_trade_no', $out_trade_no)
            ->update([
                'status' => 2
            ]);
        if (!empty($result['err_code_des'])) {
            return errorJson($result['err_code_des']);
        } elseif (!empty($result['return_msg'])) {
            return errorJson($result['return_msg']);
        } else {
            return errorJson('收款失败，请重试');
        }
    }

    /**
     * 支付宝 - 支付成功
     */
    private function alipaySuccess($out_trade_no, $payConfig, $shop, $result)
    {
        //记录优惠信息
        /*$coupon_count = 0;
        $coupon_fee_nocash = 0;
        $coupon_id_nocash = '';
        $coupon_fee_cash = 0;
        $coupon_id_cash = '';
        if($result['fund_bill_list']){
            foreach ($result['fund_bill_list'] as $v){
                if($v['fund_channel']=='MDISCOUNT'){
                    //商户优惠券
                    $coupon_fee_nocash += $v['amount'];
                    $coupon_count ++;
                }else if($v['fund_channel']=='MCOUPON'){
                    //商户红包
                    $coupon_fee_nocash += $v['amount'];
                    $coupon_count ++;
                }else if($v['fund_channel']=='DISCOUNT'){
                    //折扣券
                    $coupon_fee_nocash += $v['amount'];
                    $coupon_count ++;
                }else if($v['fund_channel']=='COUPON'){
                    //支付宝红包
                    $coupon_fee_cash += $v['amount'];
                    $coupon_count ++;
                }
            }
        }
        $save['ordernumber_channel'] = $alicodepay['trade_no'];
        $save['status'] = 1;
        $save['paytype'] = 1;
        $save['paytime'] = time();

        $save['coupon_count'] = $coupon_count;
        $save['coupon_fee_nocash'] = $coupon_fee_nocash;
        $save['coupon_id_nocash'] = $coupon_id_nocash;
        $save['coupon_fee_cash'] = $coupon_fee_cash;
        $save['coupon_id_cash'] = $coupon_id_cash;
        $save['settlement_total_fee'] = $alicodepay['receipt_amount'];
        $save['buyer_user_id'] = $alicodepay['buyer_user_id'];
        $save['buyer_user_name'] = $alicodepay['buyer_logon_id'];




        Db::name('order')
            ->where('out_trade_no', $out_trade_no)
            ->update([
                'status' => 1,
                'transaction_id' => $result ['transaction_id'],
                'coupon_count' => $coupon_count,
                'coupon_fee_nocash' => $coupon_fee_nocash,
                'coupon_fee_cash' => $coupon_fee_cash,
                'settlement_total_fee' => $settlement_total_fee,
                'fee' => $fee * 100,
                'fee_agent' => $fee_agent * 100,
                'pay_time' => time()
            ]);

        return successJson('', '收款成功');*/
    }

    /**
     * 支付宝 - 支付失败
     */
    private function alipayFail($out_trade_no, $result = [])
    {
        /*Db::name('order')
            ->where('out_trade_no', $out_trade_no)
            ->update([
                'status' => 2
            ]);
        if (!empty($result['err_code_des'])) {
            return errorJson($result['err_code_des']);
        } elseif (!empty($result['return_msg'])) {
            return errorJson($result['return_msg']);
        } else {
            return errorJson('收款失败，请重试');
        }*/
    }

    /**
     * 创建订单号
     */
    private function createOrderNo($pay_type)
    {
        if ($pay_type == 'alipay') {
            return 'A' . date('YmdHis') . rand(1000, 9999);
        } elseif ($pay_type == 'wxpay') {
            return 'W' . date('YmdHis') . rand(1000, 9999);
        } elseif ($pay_type == 'refund') {
            return 'T' . date('YmdHis') . rand(1000, 9999);
        }
    }

    /**
     * 计算手续费
     * rate是放大10000的整数
     * money是正常值
     */
    private function calcOrderFee($rate = 0, $money = 0)
    {
        if (empty($rate) || empty($money)) {
            return 0;
        }
        return round($rate * $money / 10000, 2);
    }

    /**
     * 计算代理分润
     * rate是放大10000的整数
     * money是正常值
     * rebate是放大100的整数
     */
    private function calcAgentFee($rate = 0, $money = 0, $rebate = 0)
    {
        if (empty($rate) || $rate <= 20 || empty($money) || empty($rebate)) {
            return 0;
        }
        return round(($rate - 20) * $money * $rebate / 10000 / 100, 2);
    }

    public function dorefund()
    {
        $out_trade_no = input('out_trade_no', '', 'trim');
        if (empty($out_trade_no)) {
            return errorJson('请输入订单号');
        }
        $order = Db::name('order')
            ->where([
                ['shop_id', '=', self::$shop['id']],
                ['out_trade_no|transaction_id', '=', $out_trade_no],
                ['order_type', '=', 'pay']
            ])
            ->find();
        if (!$order) {
            return errorJson('系统内没有找到此订单');
        }
        if ($order['status'] == 0) {
            return errorJson('此订单尚未付款');
        }
        if ($order['is_refund'] == 1) {
            return errorJson('此订单已经退过款了');
        }

        // 退款单号
        $out_refund_no = $this->createOrderNo('refund');

        if ($order['pay_type'] == 'wxpay') {

            $payConfig = getShopPayConfig($order['shop_id'], 'wxpay');
            if (empty($payConfig['apiclient_cert']) || empty($payConfig['apiclient_key'])) {
                return errorJson('退款失败：未上传支付证书');
            }

            $input = new WxPayRefund();
            $input->SetOut_trade_no($order['out_trade_no']);
            $input->SetOut_refund_no($out_refund_no);
            $input->SetTotal_fee($order['total_fee']);
            $input->SetRefund_fee($order['total_fee']);
            $input->SetOp_user_id($payConfig['mch_id']);
            if ($payConfig['type'] == 'server') {
                $input->SetSubMchID($payConfig['sub_mch_id']);
            }

            $config = new WxPayConfig();
            $config->SetAppId($payConfig['appid']);
            $config->SetAppSecret($payConfig['appsecret']);
            $config->SetMerchantId($payConfig['mch_id']);
            if ($payConfig['type'] == 'server') {
                $config->SetSubMchID($payConfig['sub_mch_id']);
            }
            $config->SetKey($payConfig['key']);
            $config->SetSignType('HMAC-SHA256');
            $config->SetSSLCertPath($_SERVER['DOCUMENT_ROOT'] . $payConfig['apiclient_cert']);
            $config->SetSSLKeyPath($_SERVER['DOCUMENT_ROOT'] . $payConfig['apiclient_key']);
            $WxPayApi = new WxPayApi();
            $result = $WxPayApi->refund($config, $input);
            if (isset($result['result_code']) && $result['result_code'] == 'SUCCESS' && isset($result['return_code']) && $result['return_code'] == 'SUCCESS') {
                Db::name('order')
                    ->where('id', $order['id'])
                    ->update([
                        'is_refund' => 1
                    ]);
                Db::name('order')
                    ->insert([
                        'agent_id' => $order['agent_id'],
                        'shop_id' => $order['shop_id'],
                        'qrcode_id' => $order['qrcode_id'],
                        'user_id' => $order['user_id'],
                        'out_trade_no' => $out_refund_no,
                        'status' => 1,
                        'total_fee' => $order['total_fee'],
                        'order_type' => 'refund',
                        'pay_type' => 'wxpay',
                        'pay_time' => time(),
                        'transaction_id' => $result['transaction_id'],
                        'add_time' => time()
                    ]);
                return successJson('', '退款成功，款项已原路退回');
            } else {
                $message = !empty($result['err_code_des']) ? $result['err_code_des'] : (!empty($result['return_msg']) ? $result['return_msg'] : '退款失败');
                return errorJson($message);
            }
        } elseif ($order['pay_type'] == 'alipay') {
            $payConfig = getShopPayConfig($order['shop_id'], 'alipay');

            $aop = get_alipay_aop($payConfig);
            $request = new AlipayTradeRefundRequest();
            $request->setBizContent(json_encode([
                'out_trade_no' => $order['out_trade_no'],
                'refund_amount' => $order['total_fee'] / 100,
            ]));
            if ($payConfig['type'] == 'server') {
                $result = $aop->execute($request, '', $payConfig['app_auth_token']);
            } elseif ($payConfig['type'] == 'shop') {
                $result = $aop->execute($request);
            }
            $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
            $resultCode = $result->$responseNode->code;
            if (!empty($resultCode) && $resultCode == 10000) {
                Db::name('order')
                    ->where('id', $order['id'])
                    ->update([
                        'is_refund' => 1
                    ]);
                Db::name('order')
                    ->insert([
                        'agent_id' => $order['agent_id'],
                        'shop_id' => $order['shop_id'],
                        'qrcode_id' => $order['qrcode_id'],
                        'user_id' => $order['user_id'],
                        'out_trade_no' => $out_refund_no,
                        'status' => 1,
                        'total_fee' => $order['total_fee'],
                        'order_type' => 'refund',
                        'pay_type' => 'alipay',
                        'pay_time' => time(),
                        'transaction_id' => $result['transaction_id'],
                        'add_time' => time()
                    ]);

                return successJson('', '退款成功');
            } else {
                return errorJson('退款失败：' . $result->$responseNode->sub_msg);
            }
        }

    }

}
